﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Media;
using Microsoft.VisualBasic;
using System.IO;
using Microsoft.Win32;
using Asomy.Model.Brushes;
using System.Timers;


namespace Asomy
{
    /// <summary>
    /// Interaction logic for MasterController.xaml
    /// </summary>
    public partial class MasterController : Window
    {
        Model.LayerHandler m_layerHandler; 
        View.PaintAreaView m_pav = new View.PaintAreaView();        
        Color m_paintColor;

        View.Input m_input = new View.Input();

        BrushHandler m_brushHandler = new BrushHandler();
        /// <summary>
        /// The brush used when painting
        /// </summary>
        Model.Brushes.Brush m_brush;
        /// <summary>
        /// A temporary brushed used to store info when in brush options
        /// </summary>
        Model.Brushes.Brush m_tempBrush;

        public int m_width;
        public int m_height;
        public int m_maxWidth = 2000;
        public int m_maxHeight = 2000;

        private byte m_aValue = 0;
        private byte m_rValue = 0;
        private byte m_gValue = 0;
        private byte m_bValue = 0;

        public int m_activeLayerID;
        

        public MasterController()
        {
            m_brush = m_brushHandler.GetSelectedBrush();

            m_width = 1024;
            m_height = 800;            

            m_layerHandler =  new Model.LayerHandler(m_width,m_height);            
            InitializeComponent();            
            Title = "UltraPaint";            

            x_alphaSlider.Value = 255;

            ChangeZoomLabelValue();                                   

            ChangePaintColor();
            
            UpdatePaintArea();

            UpdateLayersList();

            UpdateBrushList();

            m_activeLayerID = Model.LayerHandler.m_layers.Count - 1;                
        }

        #region LayerStuff

        /// <summary>
        /// Updates the listbox x_layersListBox with the layers. Top layer gets to the top of the list
        /// </summary>
        public void UpdateLayersList()
        {
            x_layersListBox.Items.Clear();            
            
            for (int i = Model.LayerHandler.m_layers.Count - 1; i >0; i--)
            {
                ListBoxItem f_boxitem = new ListBoxItem();

                View.LayerViewItem f_item = new View.LayerViewItem(Model.LayerHandler.m_layers[i]);

                f_boxitem.Content = f_item.m_content;

                x_layersListBox.Items.Add(f_boxitem);

                f_boxitem.Tag = f_item;
            }
        }

        /// <summary>
        /// If user clicks on AddLayer button it creates a popup box where user fills in layer name and then click ok if they wanna create it
        /// </summary>        
        private void AddLayer_Click(object sender, RoutedEventArgs e)
        {
            string f_inputname = "name";

            Controllers.MessagePopup f_popup = new Controllers.MessagePopup("Enter the new Layer name: ", "Create Layer", "Cancel", new string[] { f_inputname });

            f_popup.Owner = this;

            f_popup.Left = this.Left + (int)Mouse.GetPosition(this).X + -230;
            f_popup.Top = this.Top + (int)Mouse.GetPosition(this).Y -150;

            f_popup.SetInputData(f_inputname,"Layer " + (Model.LayerHandler.m_layers.Count).ToString());

            f_popup.ShowDialog();

            string f_name = f_popup.GetInputData(f_inputname);

            if (f_popup._OKclicked && f_name.Length > 0) //uses title to pass along message from my class, only because I don't know how to do it elsewhere
            {
                m_layerHandler.AddLayer(f_name);
                UpdateLayersList();
            }
        }
        /// <summary>
        /// Gets the LayerViewItem and sets m_activelayerID to the selected layers ID
        /// </summary>        
        private void LayerSelected(object sender, SelectionChangedEventArgs e)
        {
            foreach (ListBoxItem item in x_layersListBox.SelectedItems)
            {
                View.LayerViewItem f_lavit = (View.LayerViewItem)item.Tag;

                m_activeLayerID = f_lavit.m_layerID;
            }
        }

        #endregion
        //Contains::    TryToPaint, UpdatePaintArea,  ChangePaintColor, ChangeNewPaintColor
        //              x_(alpha,green,red,blue)Slider_ValueChanged,   ChangeColor_Click
        #region PaintStuff


        /// <summary>
        /// Tries to Paint when mouse was moved, position is relative to the x_paintArea grid
        /// </summary>        
        private void TryToPaint(object sender, MouseEventArgs e)
        {          
            if (Mouse.LeftButton == MouseButtonState.Pressed)
            {
                Paint();                
            }
        }

        /// <summary>
        /// This could be broken out later to a class that uses different sizes e.t.c.
        /// </summary>
        private void Paint()
        {

            int x = (int)(Mouse.GetPosition(x_paintArea).X / m_pav.m_camera.X_scale);
            int y = (int)(Mouse.GetPosition(x_paintArea).Y / m_pav.m_camera.Y_scale);

            if (x >= 0 && y >= 0)
            {
                Model.LayerHandler.PaintOnLayer(m_activeLayerID, m_brush.Draw(x, y), m_paintColor);                
            }

            UpdatePaintArea();
        }
        

        private void TryToPaint(object sender, MouseButtonEventArgs e)
        {
            Paint();            
            
        }

        /// <summary>
        /// Updates the Canvas with the latest GetImage()
        /// </summary>
        private void UpdatePaintArea()
        {            
            m_pav.DrawPaintArea(ref x_paintArea, m_layerHandler.GetImage(), m_width, m_height);           
        }        

        /// <summary>
        /// Changes the current color being used when painting
        /// </summary>
        private void ChangePaintColor()
        {
            m_paintColor = Color.FromArgb(m_aValue, m_rValue, m_gValue, m_bValue);           
            
            x_currentColorRect.Fill = ConvertColorToBrush(m_paintColor);
        }


        /// <summary>
        /// Changes the rectangle that shows the new paint color that user will get if they click on Change Color button
        /// </summary>
        private void ChangeNewPaintColor()
        {
            x_newColorRect.Fill = ConvertColorToBrush(Color.FromArgb(m_aValue, m_rValue, m_gValue, m_bValue));
        }

        /// <summary>
        /// Converts a color to a brush. It's so you can fill up stuff with certain colors
        /// </summary>              
        private System.Windows.Media.Brush ConvertColorToBrush(Color a_color)
        {
            return new SolidColorBrush(a_color);
        }

        /// <summary>
        /// Sets alpha value to new value and changes the new paintcolor rectangle so user knows what color they'll get
        /// </summary>       
        private void x_alphaSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            m_aValue = (byte)e.NewValue;

            x_alphaLabel.Content = "A value: " + m_aValue;

            ChangeNewPaintColor();
        }
        /// <summary>
        /// Sets red value to new value and changes the new paintcolor rectangle so user knows what color they'll get
        /// </summary>  
        private void x_redSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            m_rValue = (byte)e.NewValue;

            x_redLabel.Content = "R value: " + m_rValue;

            ChangeNewPaintColor();
        }

        /// <summary>
        /// Sets green value to new value and changes the new paintcolor rectangle so user knows what color they'll get
        /// </summary>  
        private void x_greenSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            m_gValue = (byte)e.NewValue;

            x_greenLabel.Content = "G value: " + m_gValue;

            ChangeNewPaintColor();
        }

        /// <summary>
        /// Sets blue value to new value and changes the new paintcolor rectangle so user knows what color they'll get
        /// </summary>  
        private void x_blueSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            m_bValue = (byte)e.NewValue;

            x_blueLabel.Content = "B value: " + m_bValue;

            ChangeNewPaintColor();
        }

        /// <summary>
        /// Changes the color of the pain brush if user clicked button ChangeColorBtn
        /// </summary>        
        private void ChangeColor_Click(object sender, RoutedEventArgs e)
        {
            ChangePaintColor();
        }

        #endregion

        //Contains: UpdateBrushList() , BrushSelected , UpdateSelectedBrush , BrushSizeChanged_Slider , 
        //UpdateSelectedBrushOptions  , ChangeActiveBrush 
        #region BrushOptions

        private void UpdateBrushList()
        {
            x_brushlistCombo.Items.Clear();

            foreach (Model.Brushes.Brush brush in m_brushHandler.m_brushes)
            {
                ComboBoxItem f_item = new ComboBoxItem();

                f_item.Content = brush.GetName();

                f_item.Tag = brush;

                x_brushlistCombo.Items.Add(f_item);
            }

            x_brushlistCombo.SelectedItem = x_brushlistCombo.Items.GetItemAt(m_brushHandler.ChosenBrushID);
        }

        private void BrushSelected(object sender, SelectionChangedEventArgs e)
        {
            ComboBoxItem item = (ComboBoxItem)x_brushlistCombo.SelectedItem;

            m_tempBrush = (Model.Brushes.Brush)item.Tag;
            m_brushHandler.ChosenBrushID = m_tempBrush.GetID();

            UpdateSelectedBrush();
        }

        private void UpdateSelectedBrush()
        {
            x_brushOptions_nameLabel.Content = "\n" + m_tempBrush.GetName();

            x_brushOptions_sizeLabel.Content = "Size: " + m_tempBrush.GetSize();

            x_brushOptions_valueChangedSlider.Minimum = m_tempBrush.GetMinSize();
            x_brushOptions_valueChangedSlider.Maximum = m_tempBrush.GetMaxSize();
            x_brushOptions_valueChangedSlider.Value = m_tempBrush.GetSize();
        }

        private void BrushSizeChanged_Slider(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            x_brushOptions_sizeLabel.Content = "Size: " + e.NewValue;
        }

        private void UpdateSelectedBrushOptions(object sender, RoutedEventArgs e)
        {
            m_brushHandler.m_brushes[m_brushHandler.ChosenBrushID].ChangeSize((int)x_brushOptions_valueChangedSlider.Value);
        }

        private void ChangeActiveBrush_Click(object sender, RoutedEventArgs e)
        {
            UpdateSelectedBrushOptions(sender, e);
            m_brush = m_brushHandler.GetSelectedBrush();
        }

        #endregion

        //Contains :: KeysDown,  KeysUp
        #region HotKeys
        /// <summary>
        /// Used to set what keys are down is used for hotkeys
        /// </summary>        
        private void KeysDown(object sender, KeyEventArgs e)
        {
            m_input.SetKeys(e);

            if (m_input.m_zoomIn)
            {
                m_pav.m_camera.ZoomIn();

                ChangeZoomLabelValue();

                m_pav.DrawPaintArea(ref x_paintArea, m_layerHandler.m_image, m_width, m_height);
            }
            else if (m_input.m_zoomOut)
            {
                m_pav.m_camera.ZoomOut();

                ChangeZoomLabelValue();

                m_pav.DrawPaintArea(ref x_paintArea, m_layerHandler.m_image, m_width, m_height);
            }
        }

        private void ChangeZoomLabelValue()
        {
            x_zoomLabel.Content = "Zoom: " + m_pav.m_camera.Zoom * 100 + " %";
        }

        private void KeysUp(object sender, KeyEventArgs e)
        {
            m_input.UnsetKeys(e);
        }

        #endregion


        /// <summary>
        /// This one checks if Layerhandler.m_updatesNeeded is true or not. And currently it's set on mouse move. 
        /// I have ideas for a possible timer every 0.5 sec or something maybe but I don't know what's best. This
        /// feels like a bad solution aswell but whatever a.t.m:! 
        /// </summary>        
        private void CheckForUpdates(object sender, MouseEventArgs e)
        {
            if (Model.LayerHandler.m_needUpdate)
            {
                UpdateLayersList();

                m_layerHandler.CreateImage();

                UpdatePaintArea();
                Model.LayerHandler.m_needUpdate = false;
            }
        }

        //SaveImage_Click and OpenImage_Click       
        #region ImageSaveOpen
        private void SaveImage_Click(object sender, RoutedEventArgs e)
        {
            string f_inputName = "Name";

            Controllers.MessagePopup mp = new Asomy.Controllers.MessagePopup("Enter ze filename: ", "Save image", "Cancel", new string[] { f_inputName });

            mp.Owner = this;

            mp.Left = this.Left + (int)Mouse.GetPosition(this).X;
            mp.Top = this.Top + (int)Mouse.GetPosition(this).Y + 25;

            mp.ShowDialog();

            string f_name = mp.GetInputData(f_inputName);

            if (mp._OKclicked && f_name.Length > 0)
            {
                BitmapSource img = m_pav.CreateImage(m_width, m_height, m_layerHandler.GetFinalImage());

                using (FileStream fileStream = new FileStream(f_name + ".png", FileMode.Create))
                {
                    BitmapEncoder encoder = new PngBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(img));
                    encoder.Save(fileStream);
                }
            }
        }

        private void OpenImage_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                OpenFileDialog ofd = new OpenFileDialog();

                ofd.DefaultExt = ".png";
                ofd.Filter = "Image Files (.png)|*.png";

                byte[] f_img;

                bool? b = ofd.ShowDialog();

                if (b != null && b.Value == true)
                {
                    if (ofd.FileName.EndsWith("png"))
                    {                       
                        //ofd.Get
                        using (Stream sr = ofd.OpenFile())
                        {
                            PngBitmapDecoder decoder = new PngBitmapDecoder(sr, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);

                            if (decoder.Frames != null && decoder.Frames.Count > 0)
                            {
                                BitmapSource source = decoder.Frames[0];
                                int f_bitsPerPixel = source.Format.BitsPerPixel;
                                if (source.PixelWidth < 2000 && source.PixelHeight < 2000 && f_bitsPerPixel > 23 && f_bitsPerPixel < 33)
                                {
                                    int stride = source.PixelWidth * ((f_bitsPerPixel + 7) / 8);

                                    int f_width = source.PixelWidth;
                                    int f_height = source.PixelHeight;

                                    f_img = new byte[stride * source.PixelHeight];

                                    source.CopyPixels(f_img, stride, 0);

                                    Color[,] f_colorMatrix = new Color[f_width, f_height];

                                    int ofs = 0;

                                    for (int y = 0; y < f_height; y++)
                                    {
                                        for (int x = 0; x < f_width; x++)
                                        {
                                            f_colorMatrix[x, y] = new Color();

                                            f_colorMatrix[x, y].B = f_img[ofs++];
                                            f_colorMatrix[x, y].G = f_img[ofs++];
                                            f_colorMatrix[x, y].R = f_img[ofs++];
                                            if (f_bitsPerPixel == 32)
                                            {
                                                f_colorMatrix[x, y].A = f_img[ofs++];
                                            }
                                            else
                                            {
                                                f_colorMatrix[x, y].A = 255;
                                            }
                                        }
                                    }

                                    m_width = f_width;
                                    m_height = f_height;

                                    m_layerHandler = new Model.LayerHandler(m_width, m_height, f_colorMatrix);

                                }
                            }
                        }                        
                    }
                }
            }
            catch (Exception ex)
            {

            }
        }

        private void NewImage_Click(object sender, RoutedEventArgs e)
        {
            string f_width = "width";
            string f_height = "height";

            Controllers.MessagePopup mp = new Asomy.Controllers.MessagePopup("Enter the size of the image", "New Image", "Cancel", new string[] { f_width,f_height });

            mp.Owner = this;

            mp.ShowDialog();

            string width_result = mp.GetInputData(f_width);
            string height_result = mp.GetInputData(f_height);

            if (mp._OKclicked && (width_result.Length > 0 && width_result.Length < 5) && (height_result.Length > 0 && height_result.Length < 5))
            {
                try
                {
                    int f_w = Convert.ToInt32(width_result);
                    int f_h = Convert.ToInt32(height_result);

                    if (f_w > 0 && f_w <= m_maxWidth && f_h > 0 && f_h <= m_maxHeight)
                    {
                        m_width = f_w;
                        m_height = f_h;

                        m_layerHandler = new Model.LayerHandler(m_width, m_height);

                        UpdatePaintArea();
                    }
                }
                catch (Exception ex)
                {

                }
            }
        }

        #endregion

        private void x_paintArea_MouseEnter(object sender, MouseEventArgs e)
        {
            //Mouse.OverrideCursor = Cursors.None;
        }

        private void x_paintArea_MouseLeave(object sender, MouseEventArgs e)
        {
            //Mouse.OverrideCursor = Cursors.Arrow;
        }

        
        
    }
}
